//
//  APIBaseService.m
//  BaoZhangWang
//
//  Created by yyy on 2016/12/13.
//  Copyright © 2016年 yyy. All rights reserved.
//

#import "APIBaseService.h"
#import "AFURLResponseSerialization.h"
#import "AFHTTPSessionManager.h"
#import "APIBaseManager.h"
#import "URLResponseInternal.h"

@interface APIBaseService ()

@property (nonatomic, strong) AFHTTPSessionManager *HTTPRequestManager;
@property (nonatomic, strong) AFHTTPSessionManager *JSONRequestManager;
@property (nonatomic, strong) AFHTTPSessionManager *XMLRequestManager;
@property (nonatomic, strong) NSMutableDictionary *dispatchTable;

@end

@implementation APIBaseService

+ (instancetype)service {
    static APIBaseService *baseService__ = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        baseService__ = [[APIBaseService alloc] init];
    });
    return baseService__;
}

- (NSMutableURLRequest *)requestWithAPIManager:(APIBaseManager <APIManager> *)manager error:(NSError **)serializationError {
    NSString *requestString = [manager.APIBaseUrl stringByAppendingPathComponent:manager.APIPath];
    requestString = [NSString stringWithFormat:@"%@://%@", manager.APIScheme, requestString];
    id apiparams = [manager.requestParam copy];
    AFHTTPRequestSerializer *serializer = nil;
    if (manager.APIRequestSerializerType == kAPIRequestSerializerTypeJSON) {
        serializer = self.JSONRequestManager.requestSerializer;
    } else {
        serializer = self.HTTPRequestManager.requestSerializer;
    }
    NSString *method = [self methodStringWithAPIMethod:manager.APIMethod];
    NSMutableURLRequest *request;
    if (manager.constructingBodyBlock) {
        request = [serializer multipartFormRequestWithMethod:method URLString:requestString parameters:apiparams constructingBodyWithBlock:manager.constructingBodyBlock error:serializationError];
    } else {
        request = [serializer requestWithMethod:method URLString:requestString parameters:apiparams error:serializationError];
    }
    
    if (*serializationError == nil) {
        request.timeoutInterval = manager.APIHTTPRequestTimeoutInterval;
        if (manager.APIHTTPHeader && [manager.APIHTTPHeader isKindOfClass:[NSDictionary class]]) {
            NSMutableDictionary *mdic = [request.allHTTPHeaderFields mutableCopy];
            [mdic addEntriesFromDictionary:manager.APIHTTPHeader];
            [request setAllHTTPHeaderFields:mdic];
        }
        request = [manager APIReformRequest:request];
    }
    return request;
}

- (NSInteger)callAPIManager:(APIBaseManager <APIManager> *)manager complete:(kServiceCallback)complete {
    NSError *serializationError = nil;
    NSURLRequest *request = [self requestWithAPIManager:manager error:&serializationError];
    __block URLResponse *urlResponse = [URLResponse new];
    urlResponse.request = request;
    urlResponse.requestParams = [manager.requestParam copy];
    if (serializationError) {
        if (complete) {
            urlResponse.error = serializationError;
            complete(urlResponse);
        }
        return 0;
    }
    __block NSURLSessionDataTask *dataTask = nil;
    __weak __typeof__(self) weakSelf = self;
    __weak __typeof__(manager) weakManager = manager;
    void (^completionHandler)(NSURLResponse *response, id _Nullable responseObject, NSError *_Nullable error) = nil;
    if (complete) {
        completionHandler = ^(NSURLResponse *response, id _Nullable responseObject, NSError *_Nullable error) {
            __strong __typeof__(weakSelf) self = weakSelf;
            __strong __typeof__(weakManager) manager = weakManager;
            if (!self || !manager) {
                return;
            }
            urlResponse.response = response;
            urlResponse.responseObject = responseObject;
            urlResponse.error = error;
            urlResponse.requestIdentifier = [dataTask taskIdentifier];
            [self.dispatchTable removeObjectForKey:@([dataTask taskIdentifier])];
            if ([responseObject isKindOfClass:[NSData class]]) {
                NSError *__autoreleasing serializationError = nil;
                switch (manager.APIResponseSerializerType) {
                    case kAPIResponseSerializerTypeHTTP:
                        // Default serializer. Do nothing.
                        break;
                    case kAPIResponseSerializerTypeJSON:
                        responseObject = [self.JSONRequestManager.responseSerializer responseObjectForResponse:response data:responseObject error:&serializationError];
                        break;
                    case kAPIResponseSerializerTypeTypeXMLParser:
                        responseObject = [self.XMLRequestManager.responseSerializer responseObjectForResponse:response data:responseObject error:&serializationError];
                        break;
                }
                if (serializationError) {
                    urlResponse.error = serializationError;
                } else {
                    urlResponse.responseObject = responseObject;
                }
            }
            complete(urlResponse);
        };
    }
    AFHTTPSessionManager *sessionManager = [self sessionManagerWithResponseSerializerType:manager.APIResponseSerializerType];
    if (manager.constructingBodyBlock) {
        dataTask = [sessionManager uploadTaskWithStreamedRequest:request progress:manager.uploadProgressBlock completionHandler:completionHandler];
    } else {
        dataTask = [sessionManager dataTaskWithRequest:request uploadProgress:manager.uploadProgressBlock downloadProgress:manager.downloadProgressBlock completionHandler:completionHandler];
    }
    
    NSInteger requestID = 0;
    if (dataTask) {
        requestID = [dataTask taskIdentifier];
        if (@available(iOS 8, *)) {
            dataTask.priority = [self dataTaskPriorityWithAPIRequestPriority:manager.APIRequestPriority];
        }
        [dataTask resume];
        [self.dispatchTable setObject:dataTask forKey:@(requestID)];
    }
    return requestID;
}

- (void)cancelRequestWithRequestID:(NSNumber *)requestID {
#ifdef DEBUG
    NSLog(@"cancel request with ID(%@)", requestID);
#endif
    NSURLSessionDataTask *requestOperation = self.dispatchTable[requestID];
    [requestOperation cancel];
    [self.dispatchTable removeObjectForKey:requestID];
}

- (void)cancelRequestWithRequestIDList:(NSArray *)requestIDList {
    for (NSNumber *requestID in requestIDList) {
        [self cancelRequestWithRequestID:requestID];
    }
}

#pragma mark - private code

- (AFHTTPSessionManager *)sessionManagerWithResponseSerializerType:(APIResponseSerializerType)type {
    AFHTTPSessionManager *sessionManager = nil;
    switch (type) {
        case kAPIResponseSerializerTypeHTTP:
            sessionManager = self.HTTPRequestManager;
            break;
        case kAPIResponseSerializerTypeJSON:
            sessionManager = self.JSONRequestManager;
            break;
        case kAPIResponseSerializerTypeTypeXMLParser:
            sessionManager = self.XMLRequestManager;
            break;
        default:
            sessionManager = self.XMLRequestManager;
            break;
    }
    return sessionManager;
}

- (float)dataTaskPriorityWithAPIRequestPriority:(APIRequestPriority)type {
    switch (type) {
        case kAPIRequestPriorityLow: {
            return NSURLSessionTaskPriorityLow;
        }
        case kAPIRequestPriorityHigh: {
            return NSURLSessionTaskPriorityHigh;
            break;
        }
        case kAPIRequestPriorityDefault: {
            return NSURLSessionTaskPriorityDefault;
            break;
        }
        default:
            return NSURLSessionTaskPriorityDefault;
            break;
    }
}

- (NSString *)methodStringWithAPIMethod:(APIRequestMethod)requestMethod {
    NSString *method = nil;
    switch (requestMethod) {
        case kAPIRequestMethodGET: {
            method = @"GET";
            break;
        }
        case kAPIRequestMethodPUT: {
            method = @"PUT";
            break;
        }
        case kAPIRequestMethodHEAD: {
            method = @"HEAD";
            break;
        }
        case kAPIRequestMethodPATCH: {
            method = @"PATCH";
            break;
        }
        case kAPIRequestMethodPOST: {
            method = @"POST";
            break;
        }
        case kAPIRequestMethodDELETE: {
            method = @"DELETE";
            break;
        }
        default:
            method = @"POST";
            break;
    }
    return method;
}

#pragma mark - get, set

- (NSMutableDictionary *)dispatchTable {
    if (_dispatchTable == nil) {
        _dispatchTable = [NSMutableDictionary dictionary];
    }
    return _dispatchTable;
}

- (AFHTTPSessionManager *)HTTPRequestManager {
    if (!_HTTPRequestManager) {
        AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:nil];
        manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        manager.responseSerializer = [AFHTTPResponseSerializer serializer];
        manager.completionQueue = dispatch_queue_create("com.bzw315.yyynetworking.http.processing", DISPATCH_QUEUE_CONCURRENT);
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/javascript", @"text/plain", nil];
        _HTTPRequestManager = manager;
    }
    return _HTTPRequestManager;
}

- (AFHTTPSessionManager *)JSONRequestManager {
    if (!_JSONRequestManager) {
        AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:nil];
        manager.requestSerializer = [AFJSONRequestSerializer serializer];
        manager.responseSerializer = [AFJSONResponseSerializer serializer];
        manager.completionQueue = dispatch_queue_create("com.bzw315.yyynetworking.json.processing", DISPATCH_QUEUE_CONCURRENT);
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/javascript", @"text/plain", nil];
        _JSONRequestManager = manager;
    }
    return _JSONRequestManager;
}

- (AFHTTPSessionManager *)XMLRequestManager {
    if (!_XMLRequestManager) {
        AFHTTPSessionManager *manager = [[AFHTTPSessionManager alloc] initWithBaseURL:nil];
        manager.requestSerializer = [AFHTTPRequestSerializer serializer];
        manager.responseSerializer = [AFXMLParserResponseSerializer serializer];
        manager.completionQueue = dispatch_queue_create("com.bzw315.yyynetworking.xml.processing", DISPATCH_QUEUE_CONCURRENT);
        manager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"application/json", @"text/html", @"text/json", @"text/javascript", @"text/plain", nil];
        _XMLRequestManager = manager;
    }
    return _XMLRequestManager;
}

@end
